/*******************************************************************************
 * Copyright (c) 2014 Francesco Gabbrielli
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Francesco Gabbrielli - initial API and implementation
 ******************************************************************************/
package it.francescogabbrielli.android.solidalapp;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.location.Address;
import android.os.Handler;
import android.util.Log;

import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;

public class MapUtils {
	
	private final static Map<Marker, Blinker> BLINKERS = new Hashtable<Marker, Blinker>();
	
	private static class BlinkerStatus {
		int n;
		float[] hues;
//		BitmapDescriptor[] icons;
		BlinkerStatus(float[] hues) {
			this.hues = hues;
//			icons = new BitmapDescriptor[hues.length];
//			for(int i=0; i<hues.length; i++)
//				icons[i] = BitmapDescriptorFactory.defaultMarker(hues[i]);
		}
		float blink() {
			try {return hues[n];} 
			finally {n = (n+1) % hues.length;}
		}
	}
	
	private static class Blinker extends TimerTask {
		
		private final static long DELAY = 500;
		
		private Handler handler;
		private Marker marker;
		private BlinkerStatus status;
		private boolean canceled;
		private Timer timer;
		private long start;
		private long duration;
		private float lastHue;
		
		Blinker(Context context, Marker marker, long interval, long duration, float[] hues) {
			this.handler = new Handler(context.getMainLooper());
			this.marker = marker;
			status = new BlinkerStatus(hues);
			timer = new Timer();
			start = System.currentTimeMillis();
			this.duration = duration;
			lastHue = hues[hues.length-1];
			
			// start blinking after some delay
			timer.scheduleAtFixedRate(this, DELAY, interval);
			
		}
		
		@Override
		public boolean cancel() {
			canceled = true;
			boolean ret = super.cancel();
			timer.cancel();
			return ret;
		}
		
		@Override
		public void run() {
			if(System.currentTimeMillis()-start >= duration) {
				synchronized (BLINKERS) {
					BLINKERS.remove(this);
					cancel();
				}
				handler.post(new Runnable() {
					@Override
					public void run() {
						changeIcon(marker, lastHue);
					}
				});
			} else {
				handler.post(new Runnable() {
					@Override
					public void run() {
						if (!canceled)
							changeIcon(marker, status.blink());
					}
				});
			}
		}
	}
	
	/**
	 * Blink a marker cycling through a spectrum of different hues
	 * 
	 * @param context 
	 * 		the app context
	 * @param marker
	 * 		the marker to blink
	 * @param interval
	 * 		how long each hue should display
	 * @param duration
	 * 		total duration of the blinking
	 * @param hues
	 * 		the hues
	 */
	public static void startBlinkIcon(Context context, Marker marker, long interval, long duration, float[] hues) {
		synchronized (BLINKERS) {
			stopBlinkIcon(marker, hues[hues.length-1]);
			BLINKERS.put(marker, new Blinker(context, marker, interval, duration, hues));
		}
	}

	/**
	 * Stop blinking a marker
	 * 
	 * @param marker
	 * 		the marker to stop
	 * @param hue
	 * 		the final hue to set the marker icon to
	 * 
	 */
	public static void stopBlinkIcon(Marker marker, float hue) {
		if(marker!=null && marker.isVisible())
			synchronized (BLINKERS) {
				Blinker blinker = BLINKERS.remove(marker);
				if(blinker!=null)
					blinker.cancel();
				if(hue>=0)
					changeIcon(marker, hue);
			}
	}
	
	public static void stopBlinkIcon(Marker marker) {
		stopBlinkIcon(marker, -1);
	}
	
	/**
	 * Change icon preserving info window
	 * 
	 * @param marker
	 * 		the marker
	 * @param hue
	 * 		the icon hue
	 */
	public static void changeIcon(Marker marker, float hue) {
		try {
			boolean info = marker.isInfoWindowShown();
			marker.setIcon(BitmapDescriptorFactory.defaultMarker(hue));
			if(info)
				marker.showInfoWindow();
		} catch(Exception e) {
			Log.e(SolidalApp.TAG, "Error changing icon: "+hue, e);
		}
	}
	
	public static LatLng getLocationFromString(String address)throws JSONException, IOException {

		HttpGet httpGet = new HttpGet(
				"http://maps.google.com/maps/api/geocode/json?address="
						+ address + "&ka&sensor=false");
		HttpClient client = new DefaultHttpClient();
		HttpResponse response;
		StringBuilder stringBuilder = new StringBuilder();

		try {
			response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			InputStream stream = entity.getContent();
			int b;
			while ((b = stream.read()) != -1)
				stringBuilder.append((char) b);
		} catch (ClientProtocolException e) {
			
		} 

		JSONObject jsonObject = new JSONObject();
		jsonObject = new JSONObject(stringBuilder.toString());

		double lng = ((JSONArray) jsonObject.get("results")).getJSONObject(0)
				.getJSONObject("geometry").getJSONObject("location")
				.getDouble("lng");

		double lat = ((JSONArray) jsonObject.get("results")).getJSONObject(0)
				.getJSONObject("geometry").getJSONObject("location")
				.getDouble("lat");

		return new LatLng(lat, lng);
	}

	public static List<Address> getStringFromLocation(double lat, double lng)
			throws ClientProtocolException, IOException, JSONException {

		String address = String
				.format(Locale.ENGLISH,
						"http://maps.googleapis.com/maps/api/geocode/json?latlng=%1$f,%2$f&sensor=true&language="
								+ Locale.getDefault().getCountry(), lat, lng);
		HttpGet httpGet = new HttpGet(address);
		HttpClient client = new DefaultHttpClient();
		HttpResponse response;
		StringBuilder stringBuilder = new StringBuilder();

		List<Address> retList = null;

		response = client.execute(httpGet);
		HttpEntity entity = response.getEntity();
		InputStream stream = entity.getContent();
		int b;
		while ((b = stream.read()) != -1)
			stringBuilder.append((char) b);

		JSONObject jsonObject = new JSONObject();
		jsonObject = new JSONObject(stringBuilder.toString());

		retList = new ArrayList<Address>();

		if ("OK".equalsIgnoreCase(jsonObject.getString("status"))) {
			JSONArray results = jsonObject.getJSONArray("results");
			for (int i = 0; i < results.length(); i++) {
				JSONObject result = results.getJSONObject(i);
				String indiStr = result.getString("formatted_address");
				Address addr = new Address(Locale.getDefault());
				addr.setAddressLine(0, indiStr);
				retList.add(addr);
			}
		}

		return retList;
	}

}
